package com.mars.module.oss.manager;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.mars.framework.exception.ServiceException;
import com.mars.module.admin.entity.SysOss;
import com.mars.module.admin.service.ISysOssService;
import com.mars.module.oss.common.enums.FileUploadTypeEnums;
import io.minio.*;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author Hp
 */
@Slf4j
@Component
@ConditionalOnProperty(prefix = "easy.admin", name = "fileUploadType", havingValue = "minio")
public class MinioManager {


    @Resource
    private MinioClient minioClient;

    @Resource
    private ISysOssService sysOssService;


    @Value("${easy.admin.fileUploadType}")
    private String fileUploadType;

    private SysOss sysOss;

    @PostConstruct
    public void initConfig() {
        if (fileUploadType.equals(FileUploadTypeEnums.MINIO.getType())) {
            List<SysOss> list = sysOssService.list(FileUploadTypeEnums.MINIO.getType());
            if (CollectionUtils.isEmpty(list)) {
                throw new ServiceException("oss未配置");
            }
            sysOss = list.get(0);
        }
    }


    /**
     * description: 判断bucket是否存在，不存在则创建
     */
    public void existBucket(String name) {
        try {
            boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(name).build());
            if (!exists) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(name).build());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 创建存储bucket
     *
     * @param bucketName 存储bucket名称
     * @return Boolean
     */
    public Boolean makeBucket(String bucketName) {
        try {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());

        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 删除存储bucket
     *
     * @param bucketName 存储bucket名称
     * @return Boolean
     */
    public Boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * description: 上传文件
     *
     * @param file file
     */
    public String uploadVideo(MultipartFile file) {
        String dirUrl = "video";
        String fileName = file.getOriginalFilename();
        String[] split = fileName.split("\\.");
        if (split.length > 1) {
            fileName = split[0] + "_" + System.currentTimeMillis() + "." + split[1];
        } else {
            fileName = fileName + System.currentTimeMillis();
        }
        InputStream in = null;
        try {
            in = file.getInputStream();
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(sysOss.getBucketName())
                    .object("/" + dirUrl + "/" + fileName)
                    .stream(in, in.available(), -1)
                    .contentType(getFileContentType(file)).build());

        } catch (Exception e) {
            log.error("文件上传失败" + e.getMessage());
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        String path = sysOss.getEndpoint() + "/" + sysOss.getBucketName() + "/" + dirUrl + "/" + fileName;
        log.info("文件上传成功:" + path);
        return path;
    }

    /**
     * description: 下载文件
     *
     * @param fileName fileName
     */
    public ResponseEntity<byte[]> download(String fileName) {
        ResponseEntity<byte[]> responseEntity = null;
        InputStream in = null;
        ByteArrayOutputStream out = null;
        try {
            in = minioClient.getObject(GetObjectArgs.builder().bucket(sysOss.getBucketName()).object(fileName).build());
            out = new ByteArrayOutputStream();
            IOUtils.copy(in, out);
            //封装返回值
            byte[] bytes = out.toByteArray();
            HttpHeaders headers = new HttpHeaders();
            try {
                headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            headers.setContentLength(bytes.length);
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headers.setAccessControlExposeHeaders(Arrays.asList("*"));
            responseEntity = new ResponseEntity<byte[]>(bytes, headers, HttpStatus.OK);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return responseEntity;
    }

    public InputStream getInputStream(String fileName) {
        InputStream in = null;
        try {
            return minioClient.getObject(GetObjectArgs.builder().bucket(sysOss.getBucketName()).object(fileName).build());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 批量删除文件对象
     *
     * @param bucketName 存储bucket名称
     * @param objects    对象名称集合
     */
    public Iterable<Result<DeleteError>> removeObjects(String bucketName, List<String> objects) {
        List<DeleteObject> dos = objects.stream().map(e -> new DeleteObject(e)).collect(Collectors.toList());
        Iterable<Result<DeleteError>> results = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(dos).build());
        return results;
    }

    public String uploadImage(MultipartFile file) throws Exception {
        String suffix = FilenameUtils.getExtension(file.getOriginalFilename());
        // 创建新文件名 系统时间+4个随机字符   防止出现重名覆盖现象
        //  创建的文件名就是 img 的url
        StringBuilder newName = new StringBuilder();
        newName.append(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSSS"))).append(RandomStringUtils.random(4, true, true)).append(".").append(suffix);
        //创建连接的ip 端口  账号密码
        MinioClient minioClient = MinioClient.builder().endpoint(sysOss.getEndpoint()).credentials(sysOss.getAccessKey(), sysOss.getSecretKey()).build();
        //bucket 为在minio管理界面创建的桶的名称
        PutObjectArgs bucket = PutObjectArgs.builder()
                .contentType(getFileContentType(file))
                .stream(file.getInputStream(), file.getSize(), 0).bucket(sysOss.getBucketName())
                .object("/image/" + newName).build();
        minioClient.putObject(bucket);
        return sysOss.getEndpoint() + "/" + sysOss.getBucketName() + "/image/" + newName;
    }


    public String uploadFile(MultipartFile file) throws Exception {
        String suffix = FilenameUtils.getExtension(file.getOriginalFilename());
        // 创建新文件名 系统时间+4个随机字符   防止出现重名覆盖现象
        //  创建的文件名就是 img 的url
        StringBuilder newName = new StringBuilder();
        newName.append(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSSS")))
                .append(RandomStringUtils.random(4, true, true)).append(".").append(suffix);
        StringBuilder append = newName.append(file.getOriginalFilename());
        //创建连接的ip 端口  账号密码
        MinioClient minioClient = MinioClient.builder().endpoint(sysOss.getEndpoint()).credentials(sysOss.getAccessKey(), sysOss.getSecretKey()).build();

        //bucket 为在minio管理界面创建的桶的名称
        PutObjectArgs bucket = PutObjectArgs.builder()
                .contentType(getFileContentType(file))
                .stream(file.getInputStream(), file.getSize(), 0)
                .bucket(sysOss.getBucketName()).object("/file/" + append).build();
        minioClient.putObject(bucket);
        return sysOss.getEndpoint() + "/" + sysOss.getBucketName() + "/file/" + append;
    }

    public String uploadFile(String fileType, String fileName, InputStream inputStream, long fileSize) throws Exception {
        String suffix = FilenameUtils.getExtension(fileName);
        // 创建新文件名 系统时间+4个随机字符   防止出现重名覆盖现象
        //  创建的文件名就是 img 的url
        StringBuilder newName = new StringBuilder();
        newName.append(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSSS"))).append(RandomStringUtils.random(4, true, true)).append(".").append(suffix);
        StringBuilder append = newName.append(fileName);
        //创建连接的ip 端口  账号密码
        MinioClient minioClient = MinioClient.builder().endpoint(sysOss.getEndpoint()).credentials(sysOss.getAccessKey(), sysOss.getSecretKey()).build();
        //bucket 为在minio管理界面创建的桶的名称
        PutObjectArgs bucket = PutObjectArgs.builder()
                .contentType("application/octet-stream")
                .stream(inputStream, fileSize, 0).bucket(sysOss.getBucketName())
                .object("/" + fileType + "/" + append).build();
        minioClient.putObject(bucket);
        return sysOss.getEndpoint() + "/" + sysOss.getBucketName() + "/" + fileType + "/" + append;
    }


    private static String getFileContentType(MultipartFile file) {
        String objectName = file.getOriginalFilename();
        String fileName = objectName.substring(objectName.lastIndexOf("."));
        String returnFileName = fileName.substring(fileName.lastIndexOf("."));
        if (returnFileName.equals(".jpeg") || returnFileName.equals(".png") || returnFileName.equals(".jpg")) {
            return "image/jpeg";
        } else if (returnFileName.equals(".mp4")) {
            return "video/mp4";
        } else if (returnFileName.equals(".html")) {
            return "text/html";
        } else if (returnFileName.equals(".css")) {
            return "text/css";
        } else if (returnFileName.equals(".js")) {
            return "application/javascript";
        } else if (returnFileName.equals(".pdf")) {
            return "application/pdf";
        } else if (returnFileName.equals(".txt")) {
            return "text/plain";
        } else {
            return "application/octet-stream";
        }
    }


}
